package com.mssh.login.filter;

import com.alibaba.fastjson.JSONObject;;
import com.mssh.login.util.RsaUtil;
import com.mssh.login.util.AesUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: 熳殊沙华
 * @Date: 2023/09/20/10:24
 * @Description: 请求/响应报文加解密过滤器
 */
@WebFilter(urlPatterns = "/*", filterName = "requestEncryptFilter")
public class RequestEncryptFilter implements Filter {

    private static Logger logger = LogManager.getLogger(RequestEncryptFilter.class);

    //报文是否加解密开关
    @Value("${rsa.isEncrypt: false}")
    private boolean isEncrypt;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        logger.info("RequestEncryptFilter过滤器初始化！" + isEncrypt);
    }

    @Override
    public void destroy() {}

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
//        resp.setHeader("Access-Control-Allow-Origin", "*");
//        resp.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
//        resp.setHeader("Access-Control-Allow-Headers", "content-type,x-requested-with,Authorization, x-ui-request,lang");
//        resp.setHeader("Access-Control-Max-Age", "3600");
        //判断是否为axios请求，且是否开启加解密开关
        if(isEncrypt){
            try {
                //获取请求头中ASE的秘钥秘文
                String encryptAesKey = req.getHeader("EncryptAesKey");
                //对ASE的秘钥秘文进行解密
                String aseKey = RsaUtil.desEncrypt(encryptAesKey);
                //判断是否为上传文件的请求
                if(isMultipartUploadRequest(req)){
                    //上传文件的请求不需要进行请求报文加解密
                    ResponseEncryptWrapper responseEncryptWrapper = new ResponseEncryptWrapper(resp);
                    filterChain.doFilter(req, responseEncryptWrapper);
                    //获取响应报文的byte
                    byte[] responseData = responseEncryptWrapper.getResponseData();
                    String respongStr = new String(responseData, "UTF-8");
                    writeResponse(resp, 200, respongStr, aseKey);
                }else{
                    RequestDesEncryptWrappers requestDesEncryptWrappers = new RequestDesEncryptWrappers(req, aseKey);
                    ResponseEncryptWrapper responseEncryptWrapper = new ResponseEncryptWrapper(resp);
                    filterChain.doFilter(requestDesEncryptWrappers, responseEncryptWrapper);
                    //获取响应报文的byte
                    byte[] responseData = responseEncryptWrapper.getResponseData();
                    String respongStr = new String(responseData, "UTF-8");
                    writeResponse(resp, 200, respongStr, aseKey);
                }
            } catch (Exception e){
                logger.warn("请求异常：", e);
                writeErrorResponse(resp, 200,"请求异常！");
            }
        }else{
            filterChain.doFilter(req, resp);
        }
    }

    /**
     * 判断是否为axios请求
     * @param req
     * @return
     */
    private boolean isAxiosRequest(HttpServletRequest req){
        String requestWith = req.getHeader("x-requested-with");
        if("XMLHttpRequest".equalsIgnoreCase(requestWith)){
            return true;
        }
        return false;
    }

    /**
     * 判断是否为上传文件的请求
     * @param request
     * @return
     */
    private boolean isMultipartUploadRequest(HttpServletRequest request) {
        // 根据实际情况判断是否是文件上传请求
        // 例如，可以根据请求的Content-Type或参数等信息来判断
        return request.getContentType() != null && request.getContentType().startsWith("multipart/form-data");
    }

    /**
     * 重写响应报文
     * @param response
     * @param status
     * @param responseData
     */
    private void writeResponse(HttpServletResponse response, int status, String responseData, String aseKey){
        try {
            response.reset();
            response.setContentType("application/json;charset=UTF-8");
            JSONObject returnMap = new JSONObject();
            try{
                //响应报文加密
                String secretDate = AesUtil.encryptWithKey(responseData, aseKey);
                returnMap.put("errCode", "0000");
                returnMap.put("errMsg", "响应报文加密成功！");
                returnMap.put("responseBody",secretDate);
            }catch (Exception e){
                logger.warn("响应报文加密失败：", e);
                returnMap.put("errCode", "E999");
                returnMap.put("errMsg", "响应报文加密失败！");
            }
            response.setStatus(status);
            response.getWriter().write(returnMap.toJSONString());
        } catch (IOException e) {
            logger.warn("重写响应报文异常：", e);
        }
    }

    /**
     * 返回请求报文解密异常的报文
     * @param response
     * @param status
     * @param errMsg
     */
    private void writeErrorResponse(HttpServletResponse response, int status, String errMsg){
        try {
            JSONObject returnMap = new JSONObject();
            returnMap.put("errCode", "E999");
            returnMap.put("errMsg", errMsg);
            response.reset();
            response.setContentType("application/json;charset=UTF-8");
            response.setStatus(status);
            response.getWriter().write(returnMap.toJSONString());
        } catch (IOException e) {
            logger.warn("重写响应报文异常：", e);
        }
    }
}
